package goquery

import 

// Filter reduces the set of matched elements to those that match the selector string.
// It returns a new Selection object for this subset of matching elements.
func ( *Selection) ( string) *Selection {
	return .FilterMatcher(compileMatcher())
}

// FilterMatcher reduces the set of matched elements to those that match
// the given matcher. It returns a new Selection object for this subset
// of matching elements.
func ( *Selection) ( Matcher) *Selection {
	return pushStack(, winnow(, , true))
}

// Not removes elements from the Selection that match the selector string.
// It returns a new Selection object with the matching elements removed.
func ( *Selection) ( string) *Selection {
	return .NotMatcher(compileMatcher())
}

// NotMatcher removes elements from the Selection that match the given matcher.
// It returns a new Selection object with the matching elements removed.
func ( *Selection) ( Matcher) *Selection {
	return pushStack(, winnow(, , false))
}

// FilterFunction reduces the set of matched elements to those that pass the function's test.
// It returns a new Selection object for this subset of elements.
func ( *Selection) ( func(int, *Selection) bool) *Selection {
	return pushStack(, winnowFunction(, , true))
}

// NotFunction removes elements from the Selection that pass the function's test.
// It returns a new Selection object with the matching elements removed.
func ( *Selection) ( func(int, *Selection) bool) *Selection {
	return pushStack(, winnowFunction(, , false))
}

// FilterNodes reduces the set of matched elements to those that match the specified nodes.
// It returns a new Selection object for this subset of elements.
func ( *Selection) ( ...*html.Node) *Selection {
	return pushStack(, winnowNodes(, , true))
}

// NotNodes removes elements from the Selection that match the specified nodes.
// It returns a new Selection object with the matching elements removed.
func ( *Selection) ( ...*html.Node) *Selection {
	return pushStack(, winnowNodes(, , false))
}

// FilterSelection reduces the set of matched elements to those that match a
// node in the specified Selection object.
// It returns a new Selection object for this subset of elements.
func ( *Selection) ( *Selection) *Selection {
	if  == nil {
		return pushStack(, winnowNodes(, nil, true))
	}
	return pushStack(, winnowNodes(, .Nodes, true))
}

// NotSelection removes elements from the Selection that match a node in the specified
// Selection object. It returns a new Selection object with the matching elements removed.
func ( *Selection) ( *Selection) *Selection {
	if  == nil {
		return pushStack(, winnowNodes(, nil, false))
	}
	return pushStack(, winnowNodes(, .Nodes, false))
}

// Intersection is an alias for FilterSelection.
func ( *Selection) ( *Selection) *Selection {
	return .FilterSelection()
}

// Has reduces the set of matched elements to those that have a descendant
// that matches the selector.
// It returns a new Selection object with the matching elements.
func ( *Selection) ( string) *Selection {
	return .HasSelection(.document.Find())
}

// HasMatcher reduces the set of matched elements to those that have a descendant
// that matches the matcher.
// It returns a new Selection object with the matching elements.
func ( *Selection) ( Matcher) *Selection {
	return .HasSelection(.document.FindMatcher())
}

// HasNodes reduces the set of matched elements to those that have a
// descendant that matches one of the nodes.
// It returns a new Selection object with the matching elements.
func ( *Selection) ( ...*html.Node) *Selection {
	return .FilterFunction(func( int,  *Selection) bool {
		// Add all nodes that contain one of the specified nodes
		for ,  := range  {
			if .Contains() {
				return true
			}
		}
		return false
	})
}

// HasSelection reduces the set of matched elements to those that have a
// descendant that matches one of the nodes of the specified Selection object.
// It returns a new Selection object with the matching elements.
func ( *Selection) ( *Selection) *Selection {
	if  == nil {
		return .HasNodes()
	}
	return .HasNodes(.Nodes...)
}

// End ends the most recent filtering operation in the current chain and
// returns the set of matched elements to its previous state.
func ( *Selection) () *Selection {
	if .prevSel != nil {
		return .prevSel
	}
	return newEmptySelection(.document)
}

// Filter based on the matcher, and the indicator to keep (Filter) or
// to get rid of (Not) the matching elements.
func winnow( *Selection,  Matcher,  bool) []*html.Node {
	// Optimize if keep is requested
	if  {
		return .Filter(.Nodes)
	}
	// Use grep
	return grep(, func( int,  *Selection) bool {
		return !.Match(.Get(0))
	})
}

// Filter based on an array of nodes, and the indicator to keep (Filter) or
// to get rid of (Not) the matching elements.
func winnowNodes( *Selection,  []*html.Node,  bool) []*html.Node {
	if len()+len(.Nodes) < minNodesForSet {
		return grep(, func( int,  *Selection) bool {
			return isInSlice(, .Get(0)) == 
		})
	}

	 := make(map[*html.Node]bool)
	for ,  := range  {
		[] = true
	}
	return grep(, func( int,  *Selection) bool {
		return [.Get(0)] == 
	})
}

// Filter based on a function test, and the indicator to keep (Filter) or
// to get rid of (Not) the matching elements.
func winnowFunction( *Selection,  func(int, *Selection) bool,  bool) []*html.Node {
	return grep(, func( int,  *Selection) bool {
		return (, ) == 
	})
}